﻿using ClientBackBone.EDDN;
using ClientBackBone.EDDN.Profiles;
using System.ComponentModel;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace ShipsLog.Model
{
    public sealed class ShipsModelControl : INotifyPropertyChanged, IDisposable
    {
        private bool updating;
        private TradeData tradeData;
        private Ship currentShip;
        private FolderWatcher watcher;
        private string imageFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) + @"\Frontier Developments\Elite Dangerous";

        public string ImageFolder
        {
            get { return imageFolder; }
        }

        private CompaionConnector compaionConnector;
        public ShipsModelControl()
        {
           
        }

        public void Init()
        {
            if (!Directory.Exists(imageFolder))
            {
                Directory.CreateDirectory(imageFolder);
                // uncomment the following if you elite dangerous process can't write to the folder when created by this app.
                //var x = new DirectoryInfo(imageFolder);
                //x.GetAccessControl().AddAccessRule(
                //    new System.Security.AccessControl.FileSystemAccessRule(
                //        "Everyone",  
                //        System.Security.AccessControl.FileSystemRights.Read | System.Security.AccessControl.FileSystemRights.Write, 
                //        System.Security.AccessControl.AccessControlType.Allow));
            }
            this.watcher = new FolderWatcher(imageFolder,"*.bmp");
            this.watcher.NewFile += watcher_NewFile;
            this.compaionConnector = new CompaionConnector();
            this.compaionConnector.ConfirmationRequired += compaionConnector_ConfirmationRequired;
            this.compaionConnector.LoginRequired += compaionConnector_LoginRequired;
            
            if (File.Exists("trade.dat"))
            {
                using (Stream file = File.OpenRead("trade.dat"))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    this.tradeData = formatter.Deserialize(file) as TradeData;
                }
            }
            else
            {
                this.tradeData = new TradeData();
            }

            var nextHistory = new List<Commander>();


            foreach (var file in Directory.GetFiles("./", "*.json"))
            {
                string fileName = Path.GetFileNameWithoutExtension(file);
                if (fileName != "UserDump")
                {
                    var fileTime = DateTime.ParseExact(fileName, "yyyyMMddHHmmssfff", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal);
                    string jasonData = File.ReadAllText(file);
                    nextHistory.Add(this.compaionConnector.GetCommander(fileTime, jasonData));
                }
            }

            this.History = nextHistory;
            this.Update(true);

        }

        void watcher_NewFile(object sender, EventArgs<string> e)
        {
            this.RaisePropertyChanged("ScreenGrab");
        }

        void compaionConnector_LoginRequired(object sender, EventArgs e)
        {
            RaiseUserNotify("companion.username");
        }

        void compaionConnector_ConfirmationRequired(object sender, EventArgs e)
        {
            RaiseUserNotify("companion.code");
        }

        private int menueMode;

        public int MenueMode
        {
            get
            {
                return this.menueMode;
            }
            set
            {
                this.menueMode = value;
                this.RaisePropertyChanged("MenueMode");
            }
        }        

        public IList<Commander> History
        {
            get;
            private set;
        }


        public bool IsUpdating
        {
            get
            {
                return this.updating;
            }
            set
            {
                this.updating = value;
                this.RaisePropertyChanged("IsUpdating");
            }
        }

        public TradeData TradeData
        {
            get
            {
                return this.tradeData;
            }
            set
            {
                this.tradeData = value;
                this.RaisePropertyChanged("TradeData");
            }
        }

        private Commander commander;

        public Commander Commander
        {
            get
            {
                return this.commander;
            }
            set
            {
                this.commander = value;
                this.RaisePropertyChanged("Commander");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public event EventHandler<EventArgs<string>> UserNotify;

        private void RaisePropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        private void RaiseUserNotify(string message)
        {
            EventHandler<EventArgs<string>> handler = this.UserNotify;
            if (handler != null)
            {
                handler(this, new EventArgs<string>(message));
            }
        }

        public void Dispose()
        {
            
            if (this.tradeData != null && this.tradeData.Stations.Count > 0)
            {
                Save();
            }

            if (this.compaionConnector != null)
            {
                this.compaionConnector.Dispose();
            }

            if (this.watcher != null)
            {
                this.watcher.Dispose();
            }
        }

        private void Save()
        {
            if (File.Exists("trade.dat"))
            {
                if (File.Exists("trade.bak"))
                {
                    File.Delete("trade.bak");
                }

                File.Move("trade.dat", "trade.bak");
            }

            try
            {
                BinaryFormatter formatter = new BinaryFormatter();
                using (Stream file = File.OpenWrite("trade.dat"))
                {
                    formatter.Serialize(file, this.tradeData);
                    file.Flush();
                }
            }
            catch (Exception e)
            {
                var x = e;
            }
        }

        public void Update(bool forceUpdate)
        {
            this.IsUpdating = true;
            Task.Run(() =>
            {
                try
                {
                    if (this.compaionConnector.GetProfile(forceUpdate))
                    {
                        this.commander = this.compaionConnector.GetCommander();
                        var nextHistory = new List<Commander>(this.History);
                        nextHistory.Add(this.commander);
                        this.History = nextHistory;
                        this.Ship = this.compaionConnector.GetShip();
                        if (this.commander.docked && this.compaionConnector.HasCommodities())
                        {
                            this.tradeData.AddStation(this.compaionConnector.GetStationComodities());

                            this.tradeData.GetRecomendation(this.commander.currentsystem, this.commander.currentStation, this.commander.credits, this.commander.cargocapacity);
                            this.Save();

                        }
                        this.Commander = this.commander;
                    }
                }
                catch (InvalidDataException dataException)
                {
                    this.RaiseUserNotify(dataException.Message);
                }
                catch (WebException webEx)
                {
                    this.RaiseUserNotify(webEx.Message);
                }
                finally
                {
                    this.IsUpdating = false;
                }
            });
            
        }


        public string GetSingleRecomendation(string[] from, string[] to)
        {
            var rec = this.tradeData.GetSingleRecomendation(from[0], from[1], this.commander.credits, this.commander.cargocapacity, to[0], to[1]);
            if (rec == null)
            {
                return string.Empty;
            }
            else
            {
                return string.Format("Buy {0} tonnes of {1} from {2} in {3}, at a cost of {4} and sell it to {5} in {6} for a profit of {7}", rec.Amount, rec.Comodity, rec.FromStation, rec.FromSystem, rec.Cost, rec.ToStation, rec.ToSystem, rec.ForProfit);

            }
        }

        public void RemoveStation(string system, string station)
        {
            this.tradeData.RemoveStation(system, station);
        }

        public Ship Ship 
        { 
            get
            {
                return this.currentShip;
            } 
            set
            {
                if (!value.Equals(this.currentShip))
                {
                    this.currentShip = value;
                    this.RaisePropertyChanged("Ship");
                }
            }
        }

        public void ForgetMe()
        {
            

            System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings["email"].Value = string.Empty;
            config.AppSettings.Settings["password"].Value = string.Empty;
            config.AppSettings.Settings["code"].Value = string.Empty;
            config.Save(ConfigurationSaveMode.Full);
            ConfigurationManager.RefreshSection(config.AppSettings.SectionInformation.Name);
            this.compaionConnector.Dispose();
            if (File.Exists("comp.auth"))
            {
                File.Delete("comp.auth");
            }

            this.compaionConnector = new CompaionConnector();
            this.compaionConnector.ConfirmationRequired += compaionConnector_ConfirmationRequired;
            this.compaionConnector.LoginRequired += compaionConnector_LoginRequired;
            this.Update(true);
        }

        public void LoadFromOld()
        {
            this.compaionConnector.LoadFromOld();
            this.Commander = this.compaionConnector.GetCommander();
            this.Ship = this.compaionConnector.GetShip();
        }

        public void NetWorkLogging(bool p)
        {
            var proc = Process.GetProcessesByName("EliteDangerous64").FirstOrDefault();
            if (proc == null)
            {
                proc = Process.GetProcessesByName("EliteDangerous").FirstOrDefault();
            }
            if (proc == null)
            {
                var x ="bad";
            }

            var path = Path.GetDirectoryName(proc.MainModule.FileName);
            var config = path + @"\AppConfig.xml";
            var appConfig =  XDocument.Load(config);
            var networkNode = appConfig.Root.Element("Network");
            var attrib = networkNode.Attribute("VerboseLogging");
            if((attrib == null) == p)
            {
                if(p)
                {
                    networkNode.Add(new XAttribute("VerboseLogging",1));
                } else
                {
                    attrib.Remove();
                }
                appConfig.Save(config);
                
            }
            
        }


        public bool Verbose
        {
            get
            {
                // TODO
                //var proc = Process.GetProcessesByName("EliteDangerous64").FirstOrDefault();
                //if (proc == null)
                //{
                //    proc = Process.GetProcessesByName("EliteDangerous").FirstOrDefault();
                //}
                //if (proc == null)
                //{
                //    var x = "bad";
                //}

                //var path = Path.GetDirectoryName(proc.MainModule.FileName);
                //var config = path + @"\AppConfig.xml";
                //var appConfig = XDocument.Load(config);
                //var networkNode = appConfig.Root.Element("Network");
                //var attrib = networkNode.Attribute("VerboseLogging");
                //if ((attrib == null) == proc)
                //{
                //    if (proc)
                //    {
                //        networkNode.Add(new XAttribute("VerboseLogging", 1));
                //    }
                //    else
                //    {
                //        attrib.Remove();
                //    }
                //    appConfig.Save(config);
                //}
                return false;
            }
        }
    }
}